package com.wctj.api.business.cmb.service.handle;

import com.wctj.api.base.Result;
import com.wctj.api.base.enums.ApiCode;
import com.wctj.api.base.enums.PayModeEnums;
import com.wctj.api.base.exception.CustomException;
import com.wctj.api.base.utils.HttpClientUtil;
import com.wctj.api.base.utils.JacksonUtil;
import com.wctj.api.base.utils.JsonUtils;
import com.wctj.api.base.utils.TimeUtils;
import com.wctj.api.business.cmb.enums.CmbPayStatusEnums;
import com.wctj.api.business.common.dto.DdwxPaymentCfgInfoDTO;
import com.wctj.api.business.common.dto.DdwxPaymentMessageInfoDTO;
import com.wctj.api.business.common.helper.PayResponseHelper;
import com.wctj.api.business.common.service.AbstractPayManagerService;
import com.wctj.api.business.common.service.DdwxPaymentCfgInfoService;
import com.wctj.api.business.common.service.PayResponse;
import com.wctj.api.infra.cmb.VerifySigDemo;
import com.wctj.api.infra.common.bean.NoticeParams;
import com.wctj.api.infra.common.bean.NoticeRequest;
import com.wctj.api.infra.common.util.IOUtils;
import com.wctj.api.infra.web.support.HttpRequestNoticeParams;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.util.*;

/**
 * 招行-Adapay 支付回调响应模式处理
 */
@Slf4j
@Service
public class CmbPayResponse extends AbstractPayManagerService implements PayResponse {
    @Resource
    private PayResponseHelper payResponseHelper;
    @Resource
    private DdwxPaymentCfgInfoService ddwxPaymentCfgInfoService;
    @Value("${paysystem.cmb.phpCallbackBizCode:60000}")
    private String phpCallbackBizCode;

    private ThreadLocal<String> systemPlatform = new ThreadLocal<>();


    @Override
    public Boolean validResponse(NoticeParams noticeParams) {
        try {
            log.info("接受招行主动通知消息=================" + noticeParams.getBody());
            Map<String, Object> notifyRequest = noticeParams.getBody();
            //先将通知内容解密  验签
            //签名信息为报文中sigdat值的内容，先取出sigdat里面的内容待后面验签用，然后用“__signature_sigdat__”替换掉sigdat的值
            String resSign = (String) notifyRequest.get("sigdat");
            notifyRequest.put("sigdat", "__signature_sigdat__");
            //银行的公钥对上面的内容进行验签，验签算法为国密SM2，获取验签结果
            DdwxPaymentCfgInfoDTO ddwxPaymentCfgInfoDTO = ddwxPaymentCfgInfoService.queryInfoByBizCodeAndPlatform(phpCallbackBizCode,systemPlatform.get());
            String bankPubkey = ddwxPaymentCfgInfoDTO.getBankPubkey();
            //验签
            //boolean verifySign = VerifySigDemo.cmbSM2Verify(bankPubkey, notifyRequest.toString(), resSign);
            boolean verifySign = VerifySigDemo.cmbSM2Verify(bankPubkey, JacksonUtil.getNormalPatternJsonString(notifyRequest), resSign);
            log.info("CMB验签请参：data={},sign={},verifySign={}", notifyRequest, resSign, verifySign);
            return verifySign;
        } catch (Exception e) {
            log.error("招商异步回调验签异常", e);
        }
        return Boolean.FALSE;
    }

    @Override
    public String handle(HttpRequestNoticeParams requestNoticeParam) {
        return "";
    }

    @Override
    public String handle(HttpRequestNoticeParams requestNoticeParam,String payMode) {
        NoticeParams noticeParams = this.getNoticeParams(requestNoticeParam);
        try {
            if (payMode.split("_").length<2){
                log.error("回调接收异常消息：对 HTTP 回调消息解析失败！bodyStr:{},{}", noticeParams.getBodyStr(),payMode);
                throw new CustomException(ApiCode.PAY_CMB_ORDER_HANDLE_MSG_NOT_EXIST);
            }
            systemPlatform.set(payMode.split("_")[1]);
            // 消息校验
            if (validResponse(noticeParams)) {
                //TO-DO  根据消息体内 消息类型走对应的策略处理
                //{
                //    "sigtim": "20220607144100",
                //    "sigdat": "qx+eoOgfqJfd1a7rnBhY9Lu32ET6CcDGskyhAopx54nZDCASMJtULj3gK2LU8zLcIIh4SckO9ZynB3RE9M5Fvw==",
                //    "notdat": "{\"msgdat\":{\"trsInfo\":{\"nusAge\":\"用途\",\"rtnFlg\":\"S\",\"reqNbr\":\"5000021482\",\"crtBnk\":\"招商银行\",\"dbtAcc\":\"755926958510015\",\"eptTim\":\"0\",\"eptDat\":\"20220607\",\"crtNam\":\"当当科技科技\",\"busNar\":\"\",\"crtAdr\":\"\",\"dbtBbk\":\"75\",\"ntfCh2\":\"\",\"ntfCh1\":\"\",\"stlChn\":\"Q\",\"crtBbk\":\"\",\"oprDat\":\"20220607\",\"crtAcc\":\"755923867910402\",\"bnkFlg\":\"Y\",\"reqSts\":\"FIN\",\"trxSet\":\"BHP65701001PQBT\",\"dbtNam\":\"X86户口3\",\"yurRef\":\"202008286878022033\",\"ccyNbr\":\"10\",\"trsAmt\":\"1.07\"}},\"msgtyp\":\"FINS\"}",
                //    "notkey": "755926958510015",
                //    "usrnbr": "N008690007",
                //    "notnbr": "1534062647609700354",
                //    "nottyp": "YQN02030"
                //}
                Map<String, Object> notifyRequest = noticeParams.getBody();
                String cmdbEventType = (String) notifyRequest.get("nottyp");
                Map attrMap = new HashMap<>();
                attrMap.put("systemPlatform",payMode.split("_")[1]);
                noticeParams.setAttr(attrMap);
                payResponseHelper.eventTypeCmbHandle(noticeParams, cmdbEventType);
            } else {
                log.error("回调接收异常消息：对 HTTP 回调参数进行签名验证失败！bodyStr:{}", noticeParams.getBodyStr());
                throw new CustomException(ApiCode.PAY_CMB_BACK_MSG_ERROR);
            }
        }catch (Exception e){
            log.error("回调接收异常消息！bodyStr:{}", noticeParams.getBodyStr());
            log.error("回调接收异常消息！Exception:", e);
            throw new CustomException(ApiCode.PAY_CMB_ORDER_HANDLE_MSG_NOT_EXIST);
        }finally {
            systemPlatform.remove();
        }
        return noticeParams.getBodyStr();
    }

    @Override
    public NoticeParams getNoticeParams(NoticeRequest request) {
        NoticeParams noticeParams = new NoticeParams();
        try (InputStream is = request.getInputStream()) {
            String body = IOUtils.toString(is);
            noticeParams.setBodyStr(body);
            noticeParams.setBody(JacksonUtil.parse(body,Map.class));
        } catch (IOException e) {
            throw new CustomException(ApiCode.PAY_CALLBACK_PARAM_ERROR, e);
        }
        Map<String, List<String>> headers = new HashMap<>();
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String name = headerNames.nextElement();
            headers.put(name, Collections.list(request.getHeaders(name)));
        }
        noticeParams.setHeaders(headers);
        return noticeParams;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void payBack(NoticeParams noticeParams, String payOrderNum) {
        log.info("cmb payBack param：{}", noticeParams.getBodyStr());
        // 推送时间
        LocalDateTime pushTime = TimeUtils.getNow();
        int pushStatus = 0;
        // 发送通知推送
        Result result = new Result(ApiCode.CMB, JacksonUtil.parse(noticeParams.getBodyStr(),Map.class));
        String noticeBodyJsonStr = JsonUtils.toJson(result);
        DdwxPaymentCfgInfoDTO cfgInfo = ddwxPaymentCfgInfoService.queryInfoByBizCodeAndPlatform(phpCallbackBizCode,systemPlatform.get());
        String notifyUrl = cfgInfo.getNotifyUrl();
        try {
            String resultMsg = HttpClientUtil.postJSON(notifyUrl, null, noticeBodyJsonStr);
            // 转义结果
            Result rts = JacksonUtil.parse(resultMsg, Result.class);
            if (Objects.nonNull(rts) && rts.getCode() == ApiCode.OK.getCode().intValue()) {
                pushStatus = CmbPayStatusEnums.succeeded.getCode();
            } else {
                pushStatus = CmbPayStatusEnums.failed.getCode();
            }
        } catch (Exception ex) {
            log.error("招商支付回调异常", ex);
            pushStatus = CmbPayStatusEnums.failed.getCode();
        } finally {
            // message update
            DdwxPaymentMessageInfoDTO infoMsgDTO = ddwxPaymentMessageInfoService.findByPayOrderNumber(payOrderNum);
            DdwxPaymentMessageInfoDTO ddwxPaymentMessageInfoDTO = new DdwxPaymentMessageInfoDTO(infoMsgDTO.getId(), pushStatus, pushTime);
            ddwxPaymentMessageInfoDTO.setPushMsg(noticeBodyJsonStr);
            ddwxPaymentMessageInfoDTO.setNotifyRequestMsg(noticeParams.getBodyStr());
            ddwxPaymentMessageInfoService.updateById(ddwxPaymentMessageInfoDTO);
        }
        log.info("cmb payBack result：{}", noticeParams.getBodyStr());
    }

    @Override
    public PayModeEnums getPayMode() {
        return PayModeEnums.cmb;
    }
}
